/**
 *    Copyright (C) 2007-2009 DIVIS GmbH (http://www.divis-hagen.de/)
 * 
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License as published by the Free Software Foundation; either
 *    version 3 of the License, or (at your option) any later version.
 * 
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 * 
 *    You should have received a copy of the GNU Lesser General
 *    Public License along with this library; if not, write to the
 *    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 *    Boston, MA 02110-1301 USA
 */

package de.divis_hagen.json;

import de.divis_hagen.json.jsonorg.JSONEscape;
import java.io.IOException;
import java.io.Writer;
import java.util.Stack;


/**
 *
 * @author Aike J Sommer
 */
public abstract class TreeAction {

    public abstract void beginTree(JSONObjectBase o) throws JSONException;

    public abstract void endTree(JSONObjectBase o) throws JSONException;

    public abstract void beginElement(JSONObjectBase o, String name) throws JSONException;

    public abstract void endElement(JSONObjectBase o, String name) throws JSONException;


    public abstract static class DetailedTreeAction extends TreeAction {

        protected boolean includeNullElements = true;

        protected boolean includeNullArrayElements = true;

        public abstract void beginPrimitive(JSONObjectBase o) throws JSONException;

        public abstract void endPrimitive(JSONObjectBase o) throws JSONException;

        public abstract void beginObject(JSONObjectBase o) throws JSONException;

        public abstract void endObject(JSONObjectBase o) throws JSONException;

        public abstract void beginChild(String name) throws JSONException;

        public abstract void endChild(String name) throws JSONException;

        public abstract void beginArray(JSONObjectBase o) throws JSONException;

        public abstract void endArray(JSONObjectBase o) throws JSONException;

        public abstract void nextChild() throws JSONException;

        private Stack<JSONObjectBase> current = new Stack();

        private boolean firstChild;

        private boolean skipElement(boolean inArray, JSONObjectBase o) {
            if (o instanceof JSONNull) {
                if (!((inArray && includeNullArrayElements) ||
                        ((! inArray) && includeNullElements))) {
                    return true;
                }
            }
            return false;
        }

        @Override
        public void beginElement(JSONObjectBase o, String name) throws JSONException {
            if (! current.isEmpty() && skipElement(current.peek() instanceof JSONArray, o)) {
                return;
            }

            if (current.size() > 0) {
                if ((current.peek() instanceof JSONArray) ||
                        (current.peek() instanceof JSONObject)) {
                    if (!firstChild) {
                        nextChild();
                    }
                }
                if (current.peek() instanceof JSONObject) {
                    beginChild(name);
                }
            }
            current.push(o);
            firstChild = true;

            if (o instanceof JSONArray) {
                beginArray(o);
            } else if (o instanceof JSONObject) {
                beginObject(o);
            } else if (o instanceof JSONType) {
                beginPrimitive(o);
            }
        }

        @Override
        public void endElement(JSONObjectBase o, String name) throws JSONException {
            if (! current.isEmpty() && skipElement(current.peek() instanceof JSONArray, o)) {
                return;
            }

            firstChild = false;

            if (o instanceof JSONArray) {
                endArray(o);
            } else if (o instanceof JSONObject) {
                endObject(o);
            } else if (o instanceof JSONType) {
                endPrimitive(o);
            }

            current.pop();
            if (current.size() > 0 && (current.peek() instanceof JSONObject)) {
                endChild(name);
            }
        }

    }


    public static class Write extends DetailedTreeAction {

        private Writer out;

        public Write(Writer out) {
            this.out = out;
            includeNullElements = false;
//            System.out.println("New JSON-Writer");
        }

        private void write(String s) throws JSONException {
            try {
                out.write(s);
            } catch (IOException ex) {
                throw new JSONException(ex);
            }
        }

        @Override
        public void beginPrimitive(JSONObjectBase o) throws JSONException {
            if (o instanceof JSONString) {
                /*write("'" + ((JSONString) o).getValue().replace("\\", "\\\\").
                replace("'", "\\'").replace("\n", "\\n").replace("\r", "\\r") + "'"); */
                write(JSONEscape.quote(((JSONString) o).getValue()));
            } else if (o instanceof JSONType) {
                write(((JSONType) o).getValue());
            }
        }

        @Override
        public void endPrimitive(JSONObjectBase o) throws JSONException {
        }

        @Override
        public void beginObject(JSONObjectBase o) throws JSONException {
            write("{ ");

        }

        @Override
        public void endObject(JSONObjectBase o) throws JSONException {
            write(" }");
        }

        @Override
        public void beginArray(JSONObjectBase o) throws JSONException {
            write("[ ");
        }

        @Override
        public void endArray(JSONObjectBase o) throws JSONException {
            write(" ]");
        }

        @Override
        public void nextChild() throws JSONException {
            write(" , ");
        }

        @Override
        public void beginTree(JSONObjectBase o) throws JSONException {
        }

        @Override
        public void endTree(JSONObjectBase o) throws JSONException {
        }

        @Override
        public void beginChild(String name) throws JSONException {
            write(JSONEscape.quote(name) + ":");
        }

        @Override
        public void endChild(String name) throws JSONException {
        }

    }

}
